﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace MyAssetBundles {
    public class MyAssetBundlesManager : MonoBehaviour {
        protected Dictionary<string, MyAssetBundleOperation> _assetBundleOperationDic;
        protected Dictionary<string, string> _assetBundleErrorDic;
        protected List<string> _autoReleaseList;
        protected MyDependenciesManager _myDependencies;
        void Awake ()
        {
            _myDependencies = gameObject.AddComponent<MyDependenciesManager>();
            _assetBundleErrorDic = new Dictionary<string, string>();
            _assetBundleOperationDic = new Dictionary<string, MyAssetBundleOperation>();
            _autoReleaseList = new List<string>();
        }

        public IEnumerator Reset ()
        {
            yield return _myDependencies.Reset();
            _assetBundleErrorDic.Clear();
            _autoReleaseList.AddRange(GetAllOperationKey());
        }
        
        List<string> GetAllOperationKey ()
        {
            List<string> keyList = new List<string>();
            foreach (var item in _assetBundleOperationDic)
            {
                keyList.Add(item.Key);
            }
            return keyList;
        }

        IEnumerator LoadAssetBundleCoroutine (string assetBundleName, MyAssetBundleHandler handler)
        {
            MyAssetBundleOperation abOperation = null;
            _assetBundleOperationDic.TryGetValue(assetBundleName, out abOperation);
            if (abOperation == null)
            {
                abOperation = new MyAssetBundleOperation();
                abOperation.assetBundleName = assetBundleName;
                _assetBundleErrorDic.TryGetValue(assetBundleName, out abOperation.error);
                _assetBundleOperationDic.Add(assetBundleName, abOperation);
            } else
                abOperation.Retain();
            
            bool isLoad = false;
            abOperation.onEventMyAssetBundle += handler;
            if (!abOperation.isDone)
            {
                if (MyAssetBundlesUtil.ExtensionEquals(assetBundleName, MyAssetBundlesConfig.AssetBundleExtension))
                {
                    isLoad = true;
                    yield return _myDependencies.LoadDependencies(assetBundleName);
                    abOperation.operation = AssetBundle.LoadFromFileAsync(GetAssetBundlePath(assetBundleName));
                }else
                {
                    abOperation.error = string.Format("asset bundles extension must \"{0}\"", MyAssetBundlesConfig.AssetBundleExtension);
                }
            }
            yield return abOperation.RaiseEvent();
            if (isLoad)
                _myDependencies.UnloadDependencies(assetBundleName);
            if (abOperation.error != null)
            {
                _assetBundleOperationDic.Remove(assetBundleName);
                if (!_assetBundleErrorDic.ContainsKey(assetBundleName))
                {
                    _assetBundleErrorDic.Add(assetBundleName, abOperation.error);
                    Debug.LogErrorFormat("ERROR: {0} {1}", abOperation.assetBundleName, abOperation.error);
                }
            }
            yield return new WaitForEndOfFrame();
        }
        
        public void LoadAssetBundleAsync (string assetBundleName, MyAssetBundleHandler handler)
        {
            StartCoroutine(LoadAssetBundleCoroutine(assetBundleName, handler));
        }

        public IEnumerator ReloadDependencies (List<string> nameList)
        {
            yield return _myDependencies.ReloadDependencies(nameList);
        }

        public static string GetAssetBundlePath (string abName)
        {
            return MyAssetBundlesUtil.PathCombine(MyAssetBundlesConfig.AssetBundlesPath, abName);
        }

        public void Unload (string assetBundleName)
        {
            if (assetBundleName.Length == 0) return;
            _autoReleaseList.Add(assetBundleName);
        }

        public void UnloadAllUnused ()
        {
            List<string> keyList = GetAllOperationKey();
            foreach(string key in keyList)
            {
                TryUnloadAssetBundle(key);
            }
        }
        void UnloadAssetBundleAuto ()
        {
            if (_autoReleaseList == null || _autoReleaseList.Count == 0)
                return;
            
            for (int i = _autoReleaseList.Count - 1; i >=0; i--)
            {
                if (TryUnloadAssetBundle(_autoReleaseList[i]))
                    _autoReleaseList.RemoveAt(i);
            }
        }

        bool TryUnloadAssetBundle (string assetBundle)
        {
            MyAssetBundleOperation operation = null;
            _assetBundleOperationDic.TryGetValue(assetBundle, out operation);
            if (operation == null) return true;
            if (!operation.isUnused) return false;
            operation.Destroy();
            _assetBundleOperationDic.Remove(assetBundle);
            return true;
        }

        void Update ()
        {
            UnloadAssetBundleAuto();
            //Debug.Log("assetBundle : " + _assetBundleOperationDic.Count);
        }
    }
}

